#include "vobject.h"

VObject::VObject(QObject *parent) : QObject(parent)
{
    ObjPath= "";
    ObjType= DEFAULTOBJECT;
    DiffuseTexture.TUrl="";
    ACTIVETEXTURE= false;

    MCUnit= 0.0; // Esto tendra la unidad de espaciado en la reconstruccion

    SpecularTexture.TUrl="";
    ACTIVESPECULARMAP= false;

    SMOOTHSHADING= false;
    ISLIGHT= false;
    SHOWTIMEROTATION= false;
    REBUILDINGMC= false;

    DRAWBOUNDINGBOX= false;
    RENDERTHIS= true;
    DiffuseColor.setX(0.3); DiffuseColor.setY(0.3); DiffuseColor.setZ(0.3);
    ObjPosition.setX(0.0); ObjPosition.setY(0.0); ObjPosition.setZ(0.0);

    // Si se trata de una lampara
    LightColor.setX(1.0); LightColor.setY(1.0); LightColor.setZ(1.0);
    LightIntensity= 1.0;
    LightDirection.setX(0.0); LightDirection.setY(0.0); LightDirection.setZ(0.0);
    constAtt= linearAtt= expAtt= 0.0;
    coneAngle= 45.0;
    coneCos= 0.0;
    LIGHTISON= true;
    LightSpeed= 0.01;

    modelMatrix.setToIdentity();
    rotMatrix.setToIdentity();
    transMatrix.setToIdentity();
    scaleMatrix.setToIdentity();

    // Para las animaciones
    AnimTimer= new QTimer();
    AnimTimer->start(30);
    connect(AnimTimer, SIGNAL(timeout()), this, SLOT(Animate()));

    // Esto es para simular el movimiento con una velocidad correcta
    LastTime.start();
}

// Este constructor es para lamparas!
VObject::VObject(QVector3D Position, QVector3D LColor, float LIntensity)
{
    // Establecemos las propiedades de la luz
    ObjPosition.setX(Position.x()); ObjPosition.setY(Position.y()); ObjPosition.setZ(Position.z());
    LightColor.setX(LColor.x()); LightColor.setY(LColor.y()); LightColor.setZ(LColor.z());
    LightIntensity= LIntensity;
    LightSpeed= 0.01;

    // Y ahora otras referentes al objeto en si
    ObjPath= "";
    ObjType= DEFAULTOBJECT;

    MCUnit= 0.0; // Esto tendra la unidad de espaciado en la reconstruccion

    DiffuseTexture.TUrl="";
    ACTIVETEXTURE= false;

    SpecularTexture.TUrl="";
    ACTIVESPECULARMAP= false;

    SMOOTHSHADING= false;
    ISLIGHT= true;
    SHOWTIMEROTATION= false;
    DRAWBOUNDINGBOX= false;
    REBUILDINGMC= false;

    RENDERTHIS= true;
    DiffuseColor.setX(0.3); DiffuseColor.setY(0.3); DiffuseColor.setZ(0.3);

    modelMatrix.setToIdentity();
    rotMatrix.setToIdentity();
    transMatrix.setToIdentity();
    scaleMatrix.setToIdentity();

    // Para las animaciones
    AnimTimer= new QTimer();
    AnimTimer->start(30);
    connect(AnimTimer, SIGNAL(timeout()), this, SLOT(Animate()));

    // Esto es para simular el movimiento con una velocidad correcta
    LastTime.start();
}

// Esto le dira al objeto que se trata de una fuente de luz
void VObject::ThisIsLight()
{
    ISLIGHT= true;
}

bool VObject::LoadOffModel(QString url)
{
    // Primero toca abrir el archivo .off
    int n_vertices, n_faces, n_edges;

    FILE *input= fopen (url.toStdString().c_str(), "r");

    fscanf(input, "%d %d %d", &n_vertices, &n_faces, &n_edges);

    qDebug("%d vertices, %d caras, %d aristas.", n_vertices, n_faces, n_edges);

    fclose(input);
}

bool VObject::LoadObjModel(QString url)
{
    vector<unsigned int> vertexIndices, uvIndices, normalIndices;
    vector <vec3> temp_vertices;
    vector<vec2> temp_uvs;
    vector<vec3> temp_normals;
    float minx, maxx, miny, maxy, minz, maxz;
    minx= miny= minz= 99999999.0; maxx= maxy= maxz= -99999999.0;
    bool HAS_UVS= false;
    // Primero toca abrir el archivo .obj
    FILE *input= fopen (url.toStdString().c_str(), "r");

    // Abrimos correctamente el archivo?
    if (input!= NULL)
    {
        // Modificamos el ObjPath
        ObjPath= url;

        while (1)
        {
            char line[128];

            int result= fscanf(input, "%s", line);

            if (result== EOF)
                break; // Final del archivo!

            // Nombre de archivo!
            if (strcmp(line, "o")== 0)
            {
                char objName [128];

                fscanf(input, "%s", objName);

                setObjectName(QString::fromStdString(objName));
            }
            else if (strcmp(line, "v")== 0) // Vertice!
            {
                vec3 vertice;
                fscanf(input, "%f %f %f\n", &vertice.x, &vertice.y, &vertice.z);
                temp_vertices.push_back(vertice);

                // Tambien, veamos si puede ser tomado en cuenta para el bounding box
                if (vertice.x > maxx)
                    maxx= vertice.x;
                else if (vertice.x < minx)
                    minx= vertice.x;
                if (vertice.y > maxy)
                    maxy= vertice.y;
                else if (vertice.y < miny)
                    miny= vertice.y;
                if (vertice.z > maxz)
                    maxz= vertice.z;
                else if (vertice.z < minz)
                    minz= vertice.z;
            }
            else if (strcmp(line, "vt")== 0) // Coordenada UV!
            {
                HAS_UVS= true; // Asi sabremos cuando debamos indexar los vertices

                vec2 uv;
                fscanf(input, "%f %f\n", &uv.x, &uv.y);
                temp_uvs.push_back(uv);
            }
            else if (strcmp(line, "vn")== 0) // Normal!
            {
                vec3 normal;
                fscanf(input, "%f %f %f\n", &normal.x, &normal.y, &normal.z );
                temp_normals.push_back(normal);
            }
            else if (strcmp(line, "f")== 0) // Cara!
            {
                string vertex1, vertex2, vertex3;
                unsigned int vIndex[3], uvIndex[3], nIndex[3];
                int matches;

                if (HAS_UVS)
                    matches= fscanf(input, "%d/%d/%d %d/%d/%d %d/%d/%d\n",
                                            &vIndex[0], &uvIndex[0], &nIndex[0],
                                            &vIndex[1], &uvIndex[1], &nIndex[1],
                                            &vIndex[2], &uvIndex[2], &nIndex[2]);
                else
                    matches= fscanf(input, "%d//%d %d//%d %d//%d\n",
                                    &vIndex[0], &nIndex[0],
                                    &vIndex[1], &nIndex[1],
                                    &vIndex[2], &nIndex[2]);

                if (matches !=9 && matches!=6)
                {
                    printf("Error! Obj type unsupported!\n");
                    return false;
                }
                else
                {
                    if (HAS_UVS)
                    {
                        vertexIndices.push_back(vIndex[0]);
                        vertexIndices.push_back(vIndex[1]);
                        vertexIndices.push_back(vIndex[2]);
                        uvIndices    .push_back(uvIndex[0]);
                        uvIndices    .push_back(uvIndex[1]);
                        uvIndices    .push_back(uvIndex[2]);
                        normalIndices.push_back(nIndex[0]);
                        normalIndices.push_back(nIndex[1]);
                        normalIndices.push_back(nIndex[2]);
                    }
                    else
                    {
                        vertexIndices.push_back(vIndex[0]);
                        vertexIndices.push_back(vIndex[1]);
                        vertexIndices.push_back(vIndex[2]);

                        normalIndices.push_back(nIndex[0]);
                        normalIndices.push_back(nIndex[1]);
                        normalIndices.push_back(nIndex[2]);
                    }
                }
            }
            else if (strcmp(line, "s")==0) // Sombras suaves!
            {
                char value [5];
                fscanf(input, "%s", value);

                if (strcmp(value, "off")==0)
                    SMOOTHSHADING= false;
                else
                    SMOOTHSHADING= true;
            }
        }
        fclose(input);

        // Ahora toca utilizar los datos leidos para organizarlos
        for (unsigned int i=0; i<vertexIndices.size(); i++)
        {
            // Get the indices of its attributes
            unsigned int vertexIndex = vertexIndices[i];
            unsigned int uvIndex; if (HAS_UVS) uvIndex = uvIndices[i];
            unsigned int normalIndex = normalIndices[i];

            // Get the attributes thanks to the index
            vec3 vertex = temp_vertices[ vertexIndex-1 ];
            vec2 uv; if (HAS_UVS)  uv = temp_uvs[ uvIndex-1 ];
            vec3 normal = temp_normals[ normalIndex-1 ];

            // Ahora almacenamos los atributos en nuestro objeto :)
            vertices.push_back(vertex);
            if (HAS_UVS) uvC.push_back(uv);
            normals .push_back(normal);
        }

        // Ahora indexamos los vertices para el VBO
        if (HAS_UVS)
            indexer.indexVBOwuv(vertices, uvC, normals, render_indices, indexed_vertices, indexed_uvs, indexed_normals);
        else
            indexer.indexVBO(vertices, normals, render_indices, indexed_vertices, indexed_normals);

        // Y creamos los buffers para OpenGL
        vertexbuffer= new QGLBuffer(QGLBuffer::VertexBuffer);
        vertexbuffer->create();
        vertexbuffer->bind();
        vertexbuffer->setUsagePattern(QGLBuffer::StaticDraw);
        vertexbuffer->allocate(indexed_vertices.data(), indexed_vertices.size() * sizeof(vec3));

        elementbuffer= new QGLBuffer(QGLBuffer::IndexBuffer);
        elementbuffer->create();
        elementbuffer->bind();
        elementbuffer->setUsagePattern(QGLBuffer::StaticDraw);
        elementbuffer->allocate(render_indices.data(), render_indices.size() * sizeof(unsigned short));

        normalbuffer= new QGLBuffer(QGLBuffer::VertexBuffer);
        normalbuffer->create();
        normalbuffer->bind();
        normalbuffer->setUsagePattern(QGLBuffer::StaticDraw);
        normalbuffer->allocate(indexed_normals.data(), indexed_normals.size() * sizeof(vec3));

        if (HAS_UVS)
        {
            uvbuffer= new QGLBuffer (QGLBuffer::VertexBuffer);
            uvbuffer->create();
            uvbuffer->bind();
            uvbuffer->setUsagePattern(QGLBuffer::StaticDraw);
            uvbuffer->allocate(indexed_uvs.data(), indexed_uvs.size() * sizeof(vec2));
        }

        // Construimos el bounding box...
        BuildBoundingBox(minx, maxx, miny, maxy, minz, maxz);

        //DumpInfo();

        return true;
    }
    else
        qDebug("ERROR: Error en la lectura del modelo, no se puede abrir el archivo.\n");

    return false;
}

// Esta funcion carga un archivo en crudo de datos y crea un objeto haciendo marching cubes
int VObject::LoadRAWData (QString url, NORMALMODE normcalc,
                           int xsize, int ysize, int zsize,
                           int bitdepth,
                           int Isovalue,
                           int maxpoligon,
                           int xmax, int ymax, int zmax)
{
    // Primero vamos a limpiar cualquier estructura previa
    vertices.clear();
    normals.clear();

    // Creamos el cubo que va a contener el volumen de la entrada
    unsigned short int*** VolumeCube;
    VolumeCube = new unsigned short int** [zsize];



    for (int k=0; k<zsize; k++)
    {
        VolumeCube[k]= new unsigned short int* [ysize];
        for (int i=0; i<ysize; i++)
            VolumeCube[k][i]= new unsigned short int [xsize];
    }

    ifstream input;
    input.open(url.toStdString().c_str(), ios::binary | ios::in);

    if (!input.is_open())
    {
        printf("No se pudo abrir el archivo\n");
        return -1;
    }
    else
    {
        input.seekg(0, ios::beg);

        for (int k=0; k<zsize; k++)
        {
            for (int i=0; i<ysize; i++)
            {
                for (int j=0; j<xsize; j++)
                {
                    VolumeCube[k][i][j]= 0;
                    input.read((char *) &VolumeCube[k][i][j], bitdepth/8);
                }
            }
        }

        input.close();
    }

    // Leido el archivo, ahora debemos crear las estructuras de datos necesarias
    // para el algoritmo de marching cubes

    int isovalue= Isovalue;
    float Xunit, Yunit, Zunit;  // Escala de salida

    if (xsize >= ysize && xsize >= ysize) // X tiene la longitud mayor
    {
        Xunit= 1.0 / float(xsize);
        Yunit= float(ysize)/ float(xsize) / float(ysize);
        Zunit= float(ysize)/ float(xsize) / float(ysize);
    }
    else if (ysize >= xsize && ysize >= ysize) // Y tiene la longitud mayor
    {
        Xunit= float(xsize) / float(ysize) / float(xsize);
        Yunit= 1.0 / float(ysize);
        Zunit= float(ysize) / float(ysize) / float(ysize);
    }
    else if (ysize >= xsize && ysize >= ysize) // Z tiene la longitud mayor
    {
        Xunit= float(xsize) / float(ysize) / float(xsize);
        Yunit= float(ysize) / float(ysize) / float(ysize);
        Zunit= 1.0 / float(zsize);
    }

    Xunit *= 3.0;
    Yunit *= 3.0;
    Zunit *= 3.0;

    MCUnit= Xunit; // Como las 3 son iguales, da igual que unidad pongamos
    qDebug("XUnit: %f, YUnit: %f, ZUnit: %f", Xunit, Yunit, Zunit);

    int PoligonCount= 0;

    // Inicia el algoritmo!
    for (int k=0; k<zmax-1 && PoligonCount <= maxpoligon; k++) // Recorrera profundidad
    {
        for (int i=0; i<ymax-1 && PoligonCount <= maxpoligon; i++) // Recorrera altura
        {
            for (int j=0; j<xmax-1 && PoligonCount <= maxpoligon; j++) // Recorrera ancho
            {
                // Primero vamos a calcular, en el cubo actual, donde estan los 8 vertices
                // y cuales son sus valores
                MarchingCube CurrentCube;

                for (int u=0; u<8; u++)
                {
                    int currentPos [3];
                    currentPos[0]= vertexPos[u][0];
                    currentPos[1]= vertexPos[u][1];
                    currentPos[2]= vertexPos[u][2];

                    CurrentCube.vertices[u].x= float(j+currentPos[0]) * Xunit;
                    CurrentCube.vertices[u].y= float(i+currentPos[1]) * Yunit;
                    CurrentCube.vertices[u].z= float(k+currentPos[2]) * Zunit;
                    CurrentCube.values[u]= VolumeCube[k+currentPos[2]][i+currentPos[1]][j+currentPos[0]];
                }

                // Probamos cada vertice
                int caseIndex= 0; // Esto nos dira en que caso estamos
                int VertexMarker= 1;

                for (int vcount=0; vcount<8; vcount++)
                {
                    if (CurrentCube.values[vcount] <= isovalue)
                        caseIndex |= VertexMarker; // Prendera el bit del vertice actual

                    VertexMarker *= 2;
                }

                // Obtenemos las aristas de corte
                int cutInfo= 0;
                cutInfo= edgeTable[caseIndex]; // 12 bits, 0 si no hay corte, 1 si lo hay

                if (cutInfo!= 0) // Falso si ninguna arista es cortada, estamos ya sea dentro o fuera del volumen
                {
                    // Hay un maximo de 12 vertices nuevos, en caso de que todas las aristas sean cortadas
                    vec3 newVertices[12];
                    vec3 newNormals[12];
                    int edgeMarker= 1;

                    // Las siguientes 2 guias nos permitiran saber con que vertices interpolar
                    // para cada arista y con cuales valores
                    int indexGuideA [12]= {0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3};
                    int indexGuideB [12]= {1, 2, 3, 0, 5, 6, 7, 4, 4, 5, 6, 7};

                    for (int ecount=0; ecount<12; ecount++)
                    {
                        if (cutInfo & edgeMarker) // Hacemos una comparacion logica en el bit de nuestro interes
                        {
                            // Como este eje esta cortado, debemos interpolar la ubicacion del vertice en el mismo
                            vec3 newVertex, vertexA, vertexB;
                            vec3 newNormal;
                            float valueA, valueB;
                            int vA= indexGuideA[ecount];
                            int vB= indexGuideB[ecount];

                            vertexA.x= CurrentCube.vertices[vA].x;
                            vertexA.y= CurrentCube.vertices[vA].y;
                            vertexA.z= CurrentCube.vertices[vA].z;

                            vertexB.x= CurrentCube.vertices[vB].x;
                            vertexB.y= CurrentCube.vertices[vB].y;
                            vertexB.z= CurrentCube.vertices[vB].z;

                            valueA= CurrentCube.values[vA];
                            valueB= CurrentCube.values[vB];

                            float fact= float(isovalue - valueA) / float(valueB - valueA);

                            // Las siguientes comparaciones nos permitiran descartar vertices que esten "muy juntos"
                            if (fabs(isovalue-valueA) < 0.000001 ||  fabs(valueA-valueB) < 0.1)
                            {
                                newVertex.x= vertexA.x;
                                newVertex.y= vertexA.y;
                                newVertex.z= vertexA.z;
                            }
                            else if (fabs(isovalue-valueB) < 0.000001)
                            {
                                newVertex.x= vertexB.x;
                                newVertex.y= vertexB.y;
                                newVertex.z= vertexB.z;
                            }
                            else
                            {
                                // Como no hemos podido descartar casos base, toca interpolar
                                /** CALCULANDO VERTICE **/
                                newVertex.x= vertexA.x + fact * (vertexB.x - vertexA.x);
                                newVertex.y= vertexA.y + fact * (vertexB.y - vertexA.y);
                                newVertex.z= vertexA.z + fact * (vertexB.z - vertexA.z);
                            }

                            /** CALCULANDO NORMALES POR DIFERENCIAS FINITAS **/
                            if (normcalc== CENTRALDIF)
                            {
                                // Este va a ser el numero de espacios en una u otra direccion
                                int h= 1;

                                // Ahora calculemos su normal con diferencias finitas centrales
                                int x1A, x1B, x2A, x2B; // x1A y x1B son el +- de la normal A
                                                        // x2A y x2B son el +- de la normal B
                                // igual para las siguientes variables
                                int y1A, y1B, y2A, y2B;
                                int z1A, z1B, z2A, z2B;

                                // Ahora calculamos los indices de muestreo
                                x1A= j - h + vertexPos[vA][0]; x1B= j + h + vertexPos[vA][0];
                                x2A= j - h + vertexPos[vB][0]; x2B= j + h + vertexPos[vB][0];

                                y1A= i - h + vertexPos[vA][1]; y1B= i + h + vertexPos[vA][1];
                                y2A= i - h + vertexPos[vB][1]; y2B= i + h + vertexPos[vB][1];

                                z1A= k - h + vertexPos[vA][2]; z1B= k + h + vertexPos[vA][2];
                                z2A= k - h + vertexPos[vB][2]; z2B= k + h + vertexPos[vB][2];

                                // Limpiemos para evitar segfault
                                if (x1A < 0) x1A = 0; if (x1B >= xsize) x1B= xsize - 1;
                                if (x2A < 0) x2A = 0; if (x2B >= xsize) x2B= xsize - 1;

                                if (y1A < 0) y1A = 0; if (y1B >= ysize) y1B= ysize - 1;
                                if (y2A < 0) y2A = 0; if (y2B >= ysize) y2B= ysize - 1;

                                if (z1A < 0) z1A = 0; if (z1B >= zsize) z1B= zsize - 1;
                                if (z2A < 0) z2A = 0; if (z2B >= zsize) z2B= zsize - 1;

                                // Y ahora muestreamos y calculamos
                                float A1, B1, A2, B2;
                                float dX1, dY1, dZ1, dX2, dY2, dZ2;
                                int currentPos1[3], currentPos2[3]; // En este caso, current pos nos dira
                                // en que coordenadas del volumen muestrear (para las que
                                // no estan definidas. Es decir, si X esta definida,
                                // entonces nos interesa Y y Z de currentpos
                                currentPos1[0]= j + vertexPos[vA][0];
                                currentPos1[1]= i + vertexPos[vA][1];
                                currentPos1[2]= k + vertexPos[vA][2];

                                currentPos2[0]= j + vertexPos[vB][0];
                                currentPos2[1]= i + vertexPos[vB][1];
                                currentPos2[2]= k + vertexPos[vB][2];

                                if (x1A== 0 || x1B== xsize -1)
                                    dX1= 1.0 * h;
                                else
                                    dX1= 2.0 * h;
                                if (x2A== 0 || x2B== xsize - 1)
                                    dX2= 1.0 * h;
                                else
                                    dX2= 2.0 * h;

                                if (y1A== 0 || y1B== ysize -1)
                                    dY1= 1.0 * h;
                                else
                                    dY1= 2.0 * h;
                                if (y2A== 0 || y2B== ysize - 1)
                                    dY2= 1.0 * h;
                                else
                                    dY2= 2.0 * h;

                                if (z1A== 0 || z1B== zsize -1)
                                    dZ1= 1.0 * h;
                                else
                                    dZ1= 2.0 * h;
                                if (z2A== 0 || z2B== zsize - 1)
                                    dZ2= 1.0 * h;
                                else
                                    dZ2= 2.0 * h;

                                // Normal.X
                                A1= float(VolumeCube[currentPos1[2]][currentPos1[1]][x1A]);
                                B1= float(VolumeCube[currentPos1[2]][currentPos1[1]][x1B]);
                                A2= float(VolumeCube[currentPos2[2]][currentPos2[1]][x2A]);
                                B2= float(VolumeCube[currentPos2[2]][currentPos2[1]][x2B]);
                                newNormal.x= (1-fact)*(A1-B1)/dX1 + fact*(A2-B2)/dX2;

                                // Normal.Y
                                A1= float(VolumeCube[currentPos1[2]][y1A][currentPos1[0]]);
                                B1= float(VolumeCube[currentPos1[2]][y1B][currentPos1[0]]);
                                A2= float(VolumeCube[currentPos2[2]][y2A][currentPos2[0]]);
                                B2= float(VolumeCube[currentPos2[2]][y2B][currentPos2[0]]);
                                newNormal.y= (1-fact)*(A1-B1)/dY1 + fact*(A2-B2)/dY2;

                                // Normal.Z
                                A1= float(VolumeCube[z1A][currentPos1[1]][currentPos1[0]]);
                                B1= float(VolumeCube[z1B][currentPos1[1]][currentPos1[0]]);
                                A2= float(VolumeCube[z2A][currentPos2[1]][currentPos2[0]]);
                                B2= float(VolumeCube[z2B][currentPos2[1]][currentPos2[0]]);
                                newNormal.z= (1-fact)*(A1-B1)/dZ1 + fact*(A2-B2)/dZ2;

                                // Normalizamos la normal
                                float dist= sqrt(newNormal.x * newNormal.x +  newNormal.y * newNormal.y + newNormal.z * newNormal.z);
                                if (dist!=0.0)
                                {
                                    newNormal.x/= dist;
                                    newNormal.y/= dist;
                                    newNormal.z/= dist;
                                }
                            }

                            newVertices[ecount].x= newVertex.x - (xmax * Xunit);
                            newVertices[ecount].y= newVertex.y - (ymax * Yunit);
                            newVertices[ecount].z= newVertex.z - (zmax * Zunit);

                            if (normcalc== CENTRALDIF)
                            {
                                newNormals[ecount].x= newNormal.x;
                                newNormals[ecount].y= newNormal.y;
                                newNormals[ecount].z= newNormal.z;
                            }
                        }
                        else
                        {
                            // En caso contrario, el nuevo vertice tiene un valor invalido
                            newVertices[ecount].x= -1.0;
                            newVertices[ecount].y= -1.0;
                            newVertices[ecount].z= -1.0;

                            if (normcalc== CENTRALDIF)
                            {
                                newNormals[ecount].x= -1.0;
                                newNormals[ecount].y= -1.0;
                                newNormals[ecount].z= -1.0;
                            }
                        }

                        edgeMarker *= 2; // Elevamos al cuadrado la marca
                    }

                    // Ahora de acuerdo al caso, obtenemos la triangulacion correspondiente
                    // con los vertices que hemos calculado
                    vec3* auxVertex= new vec3();
                    vec3 auxNormal;
                    vec3 auxVertices[3];
                    int vCount= 0;

                    for (int triCount=0; triangulationTable[caseIndex][triCount]!=-1 && triCount<16; triCount++)
                    {
                        auxVertex->x= newVertices[triangulationTable[caseIndex][triCount]].x;
                        auxVertex->y= newVertices[triangulationTable[caseIndex][triCount]].y;
                        auxVertex->z= newVertices[triangulationTable[caseIndex][triCount]].z;
                        auxNormal.x= newNormals[triangulationTable[caseIndex][triCount]].x;
                        auxNormal.y= newNormals[triangulationTable[caseIndex][triCount]].y;
                        auxNormal.z= newNormals[triangulationTable[caseIndex][triCount]].z;

                        if (vCount<3)
                        {
                            auxVertices[vCount].x= auxVertex->x;
                            auxVertices[vCount].y= auxVertex->y;
                            auxVertices[vCount].z= auxVertex->z;

                            vCount++;

                        }

                        if (vCount==3)
                        {
                            /** CALCULO DE NORMALES POR TRIANGULO **/
                            if (normcalc== PERTRIANGLE || normcalc== PERVERTEX)
                            {
                                vec3 auxNormalT;

                                vec3 v1, v2;

                                v1.x= auxVertices[1].x - auxVertices[0].x;
                                v1.y= auxVertices[1].y - auxVertices[0].y;
                                v1.z= auxVertices[1].z - auxVertices[0].z;

                                v2.x= auxVertices[2].x - auxVertices[0].x;
                                v2.y= auxVertices[2].y - auxVertices[0].y;
                                v2.z= auxVertices[2].z - auxVertices[0].z;

                                auxNormalT.x= v1.y * v2.z - v1.z * v2.y;
                                auxNormalT.y= v1.z * v2.x - v1.x * v2.z;
                                auxNormalT.z= v1.x * v2.y - v1.y * v2.x;

                                normals.push_back(auxNormalT);
                                normals.push_back(auxNormalT);
                                normals.push_back(auxNormalT);
                            }

                                vCount= 0;

                                PoligonCount++; // Un poligono extra!
                        }

                        vertices.push_back(*auxVertex); /** Insertamos el vertice en nuestro objeto! **/

                        if (normcalc== CENTRALDIF)
                            normals.push_back(auxNormal); /** Y su normal **/

                    }
                }
            }
        }
    }

    qDebug("Vertices: %d", vertices.size());

    // Por temas de memoria, podemos prescindir del VolumeCube
    for (int k= 0; k<zsize; k++)
        for (int i= 0; i<ysize; i++)
                delete VolumeCube[k][i];

    // Terminado el algoritmo, ahora toca indexar nuestros vertices
    //if (normcalc== PERVERTEX)
   //     indexer.CalculateNormals();
   // indexer.indexVBO(vertices, normals, render_indices, indexed_vertices, indexed_normals);

    qDebug("Vertices indexados: %d", render_indices.size());

    // Por temas de memoria, podemos prescindir de los vertices y normales sin indexar
    //vertices.clear();
    //normals.clear();

    // Cargamos los buffers
    vertexbuffer= new QGLBuffer(QGLBuffer::VertexBuffer);
    vertexbuffer->create();
    vertexbuffer->bind();
    vertexbuffer->setUsagePattern(QGLBuffer::StaticDraw);
    vertexbuffer->allocate(vertices.data(), vertices.size() * sizeof(vec3));

   /* elementbuffer= new QGLBuffer(QGLBuffer::IndexBuffer);
    elementbuffer->create();
    elementbuffer->bind();
    elementbuffer->setUsagePattern(QGLBuffer::StaticDraw);
    elementbuffer->allocate(render_indices.data(), render_indices.size() * sizeof(unsigned short));
*/
    normalbuffer= new QGLBuffer(QGLBuffer::VertexBuffer);
    normalbuffer->create();
    normalbuffer->bind();
    normalbuffer->setUsagePattern(QGLBuffer::StaticDraw);
    normalbuffer->allocate(normals.data(), normals.size() * sizeof(vec3));

    // Listo!
    if (PoligonCount>maxpoligon)
        return WRONGTHRESHOLD;
    else
        return ALLOK;
}

// Esta funcion carga el vertex shader y lo compila
bool VObject::LoadVShader (QString url)
{
    vShader= new QGLShader (QGLShader::Vertex, this);
    return vShader->compileSourceFile(url);
}

// Esta funcion carga el fragment shader y lo compila
bool VObject::LoadFShader (QString url)
{
    fShader= new QGLShader (QGLShader::Fragment, this);
    return fShader->compileSourceFile(url);
}

// Esta funcion carga el vertex y fragment shader y los compila
bool VObject::LoadShaders (QString VSurl, QString FSurl)
{
    return (LoadVShader(VSurl) && LoadFShader(FSurl));
}

bool VObject::PrepareShaderProgram()
{
    bool result= true;

    ShaderProgram= new QGLShaderProgram(this);
    result= ShaderProgram->addShader(vShader);
    result= ShaderProgram->addShader(fShader);

    ShaderProgram->bindAttributeLocation("vertices", PROGRAM_VERTEX_ATTRIBUTE);
    ShaderProgram->bindAttributeLocation("normals", PROGRAM_NORMALS_ATTRIBUTE);
    ShaderProgram->bindAttributeLocation("texuv", PROGRAM_TEXCOORD_ATTRIBUTE);

    result= ShaderProgram->link();
    result= ShaderProgram->bind();

    // Tambien vamos a preparar los shaders de los BBox
    QGLShader *BBoxVShader= new QGLShader(QGLShader::Vertex, this);
    BBoxVShader->compileSourceCode("attribute highp vec4 vertices;\n"
                                   "uniform mediump mat4 projectMatrix;\n"
                                   "uniform mediump mat4 viewMatrix;\n"
                                   "uniform highp mat4 modelMatrix;\n"
                                   "void main (void)\n"
                                   "{\n"
                                   "    gl_Position= projectMatrix*viewMatrix*modelMatrix*vertices;\n"
                                   "}\n");

    QGLShader *BBoxFShader= new QGLShader(QGLShader::Fragment, this);
    BBoxFShader->compileSourceCode("uniform mediump vec3 InDiffuseColor;\n"
                                   "void main(void)\n"
                                   "{\n"
                                        "gl_FragColor=  vec4(1.0, 0.0, 0.0, 1.0);\n"
                                   "}\n");

    BBoxShaderProgram= new QGLShaderProgram(this);
    result &= BBoxShaderProgram->addShader(BBoxVShader);
    result &= BBoxShaderProgram->addShader(BBoxFShader);

    BBoxShaderProgram->bindAttributeLocation("vertices", PROGRAM_VERTEX_ATTRIBUTE);

    result &= BBoxShaderProgram->link();
    result &= BBoxShaderProgram->bind();

    return result;
}

// Esta funcion envia al shader actual la informacion de la iluminacion
void VObject::SetLightInfo(QVector3D AmbientColor, float AmbientIntensity, QVector3D Color, QVector3D Position, QVector3D Att, float Intensity)
{
    ShaderProgram->bind();

    ShaderProgram->setUniformValue("AmbientColor", AmbientColor);
    ShaderProgram->setUniformValue("AmbientIntensity", AmbientIntensity);
    ShaderProgram->setUniformValue("LightColor", Color);
    ShaderProgram->setUniformValue("LightIntensity", Intensity);
    ShaderProgram->setUniformValue("LightPosition", Position);
    ShaderProgram->setUniformValue("LightAtt", Att.x(), Att.y(), Att.z());

    ShaderProgram->release();
}

// Esta funcion renderiza el objeto en el glWidget
void VObject::Render(QMatrix4x4 projection, QMatrix4x4 view)
{
    ShaderProgram->bind();

    ShaderProgram->setUniformValue("viewMatrix", view);
    ShaderProgram->setUniformValue("projectMatrix", projection);
    ShaderProgram->setUniformValue("modelMatrix", getModelMatrix());

    // Debemos dibujar el wireframe por encima del objeto?
    ShaderProgram->setUniformValue("overlayWireframe", OVERLAYWIREFRAME);

    if (!ISLIGHT)
        ShaderProgram->setUniformValue("normalMatrix", getNormalMatrix());

    if (ACTIVETEXTURE)
    {
        ShaderProgram->setUniformValue("usetexture", 1);

        glEnableClientState(GL_TEXTURE_COORD_ARRAY);
        ShaderProgram->enableAttributeArray("texuv");
        uvbuffer->bind();
        ShaderProgram->setAttributeBuffer ("texuv", GL_FLOAT, 0, 2);
        glDisableClientState(GL_TEXTURE_COORD_ARRAY);

        //glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, DiffuseTexture.THandler);
        //glUniform1i(DiffuseTexture.TSampler, 0);
    }
    else
    {
        ShaderProgram->setUniformValue("usetexture", 0);

        if (!ISLIGHT)
            ShaderProgram->setUniformValue("InDiffuseColor", DiffuseColor);
        else
            ShaderProgram->setUniformValue("InDiffuseColor", LightColor);
    }

    if (!ISLIGHT)
    {
        glEnableClientState(GL_NORMAL_ARRAY);
        ShaderProgram->enableAttributeArray("normals");
        normalbuffer->bind();
        ShaderProgram->setAttributeBuffer ("normals", GL_FLOAT, 0, 3);
        glDisableClientState(GL_NORMAL_ARRAY);
    }

    glEnableClientState(GL_VERTEX_ARRAY);
    ShaderProgram->enableAttributeArray("vertices");
    vertexbuffer->bind();
    ShaderProgram->setAttributeBuffer("vertices", GL_FLOAT, 0, 3);
    glDisableClientState(GL_VERTEX_ARRAY);

    // Rendericemos distinto
    if (this->ObjType==MCRECONSTRUCTED)
    {
        glDrawArrays(GL_TRIANGLES, 0, vertices.size());
    }
    else
    {
        elementbuffer->bind();

        glDrawElements(GL_TRIANGLES, render_indices.size(), GL_UNSIGNED_SHORT, (void*)0);
    }
    ShaderProgram->release();

    if (DRAWBOUNDINGBOX)
        RenderBoundingBox(projection, view);

}

// Esta funcion obtiene el bounding box del objeto, lo que permitira que sea dibujado luego
void VObject::BuildBoundingBox(float minx, float maxx, float miny, float maxy, float minz, float maxz)
{
    // El cubo se construye
    BBox.corners[0].x= minx; BBox.corners[0].y= miny; BBox.corners[0].z= minz;
    BBox.corners[1].x= maxx; BBox.corners[1].y= miny; BBox.corners[1].z= minz;
    BBox.corners[2].x= maxx; BBox.corners[2].y= miny; BBox.corners[2].z= maxz;
    BBox.corners[3].x= minx; BBox.corners[3].y= miny; BBox.corners[3].z= maxz;
    BBox.corners[4].x= minx; BBox.corners[4].y= maxy; BBox.corners[4].z= minz;
    BBox.corners[5].x= maxx; BBox.corners[5].y= maxy; BBox.corners[5].z= minz;
    BBox.corners[6].x= maxx; BBox.corners[6].y= maxy; BBox.corners[6].z= maxz;
    BBox.corners[7].x= minx; BBox.corners[7].y= maxy; BBox.corners[7].z= maxz;

    // Se crea el VBO
    bboxVBuffer= new QGLBuffer(QGLBuffer::VertexBuffer);
    bboxVBuffer->create();
    bboxVBuffer->bind();
    bboxVBuffer->setUsagePattern(QGLBuffer::StaticDraw);
    bboxVBuffer->allocate(BBox.corners, 8 * sizeof(vec3));

    // Ahora armamos los indices de renderizado
    BBoxrender_indices.push_back(0); BBoxrender_indices.push_back(1);
    BBoxrender_indices.push_back(1); BBoxrender_indices.push_back(2);
    BBoxrender_indices.push_back(2); BBoxrender_indices.push_back(3);
    BBoxrender_indices.push_back(3); BBoxrender_indices.push_back(0);
    BBoxrender_indices.push_back(4); BBoxrender_indices.push_back(5);
    BBoxrender_indices.push_back(5); BBoxrender_indices.push_back(6);
    BBoxrender_indices.push_back(6); BBoxrender_indices.push_back(7);
    BBoxrender_indices.push_back(7); BBoxrender_indices.push_back(4);
    BBoxrender_indices.push_back(7); BBoxrender_indices.push_back(3);
    BBoxrender_indices.push_back(4); BBoxrender_indices.push_back(0);
    BBoxrender_indices.push_back(5); BBoxrender_indices.push_back(1);
    BBoxrender_indices.push_back(6); BBoxrender_indices.push_back(2);

    // Finalmente creamos el elementbuffer
    bboxelementbuffer= new QGLBuffer(QGLBuffer::IndexBuffer);
    bboxelementbuffer->create();
    bboxelementbuffer->bind();
    bboxelementbuffer->setUsagePattern(QGLBuffer::StaticDraw);
    bboxelementbuffer->allocate(BBoxrender_indices.data(), BBoxrender_indices.size() * sizeof(unsigned short));

    // Done!
}

// Esta funcion dibujara el bounding box del objeto!
void VObject::RenderBoundingBox(QMatrix4x4 projection, QMatrix4x4 view)
{
    BBoxShaderProgram->bind();

    BBoxShaderProgram->setUniformValue("viewMatrix", view);
    BBoxShaderProgram->setUniformValue("projectMatrix", projection);
    BBoxShaderProgram->setUniformValue("modelMatrix", getModelMatrix());

    glEnableClientState(GL_VERTEX_ARRAY);
    BBoxShaderProgram->enableAttributeArray("vertices");
    bboxVBuffer->bind();
    BBoxShaderProgram->setAttributeBuffer("vertices", GL_FLOAT, 0, 3);
    glDisableClientState(GL_VERTEX_ARRAY);

    bboxelementbuffer->bind();

    glDrawElements(GL_LINES, 24, GL_UNSIGNED_SHORT, (void*)0);

    BBoxShaderProgram->release();
}

// Las siguientes funciones permitiran alternar entre el dibujado del bounding box del objeto
void VObject::DrawBoundingBox()
{
    DRAWBOUNDINGBOX= true;
}

void VObject::HideBoundingBox()
{
    DRAWBOUNDINGBOX= false;
}

void VObject::SwitchBoundingBox()
{
    DRAWBOUNDINGBOX= !DRAWBOUNDINGBOX;
}

void VObject::SwitchBoundingBox(bool value)
{
    DRAWBOUNDINGBOX= value;
}

void VObject::SetDiffuseTexture(QString url)
{
    DiffuseTexture.TUrl= url;
    //DiffuseTexture.TSampler= glGetUniformLocation(ShaderProgram->programId(), "TSampler01");

    ACTIVETEXTURE= true;
}

void VObject::SetSpecularMap(QString url)
{
   //SpecularMapPath= url;
    ACTIVESPECULARMAP= true;
}

void VObject::SetDiffuseColor (float r, float g, float b)
{
    DiffuseColor.setX(r);
    DiffuseColor.setY(g);
    DiffuseColor.setZ(b);
}

void VObject::SetLightIntensity (float i)
{
    LightIntensity= i;
}

void VObject::SetLightColor (float r, float g, float b)
{
    LightColor.setX(r);
    LightColor.setY(g);
    LightColor.setZ(b);
}

void VObject::SetLightAtt(float c, float l, float e)
{
    constAtt= c;
    linearAtt= l;
    expAtt= e;
}

void VObject::UseTexture()
{
    ACTIVETEXTURE= true;
}

void VObject::OverlayWireframe()
{
    OVERLAYWIREFRAME= true;
}

void VObject::HideOverlayWireframe()
{
    OVERLAYWIREFRAME= false;
}

void VObject::SetObjType(int type)
{
    ObjType= type;
}

void VObject::IgnoreTexture()
{
    ACTIVETEXTURE= false;
}

void VObject::ShowThis()
{
    RENDERTHIS= true;
}

void VObject::HideThis()
{
    RENDERTHIS= false;
}

void VObject::SetAssetsPath(QString url)
{
    AssetsPath= url;
}

/** Transformaciones y afines **/
QMatrix4x4 VObject::getModelMatrix()
{
    return transMatrix * rotMatrix * scaleMatrix * modelMatrix;
}

QMatrix3x3 VObject::getNormalMatrix()
{
    return getModelMatrix().normalMatrix();
}

void VObject::ScaleObj(float factor)
{
    scaleMatrix.scale(factor);
}

void VObject::ScaleObj(float x, float y, float z)
{
    scaleMatrix.scale(x, y, z);
}

// Esta funcion simplemente traslada el objeto x/y/z unidades
void VObject::TranslateObj(float x, float y, float z)
{
    transMatrix.translate(x, y, z);

    QVector4D AuxPos;
    AuxPos.setX(0.0); AuxPos.setY(0.0); AuxPos.setZ(0.0); AuxPos.setW(1.0);
    AuxPos= transMatrix * AuxPos;

    ObjPosition.setX(AuxPos.x()); ObjPosition.setY(AuxPos.y()); ObjPosition.setZ(AuxPos.z());
}

// Esta funcion va a trasladar el objeto a una posicion especifica, partiendo desde el origen
void VObject::SetPosition(float x, float y, float z)
{
    transMatrix.setToIdentity();
    transMatrix.translate(x, y, z);
}

QVector3D VObject::GetPosition()
{
    QVector4D AuxPos;
    QVector3D Output;
    AuxPos.setX(0.0); AuxPos.setY(0.0); AuxPos.setZ(0.0); AuxPos.setW(1.0);
    AuxPos= transMatrix * AuxPos;

    Output.setX(AuxPos.x()); Output.setY(AuxPos.y()); Output.setZ(AuxPos.z());

    return Output;
}

// Funciones de informacion
int VObject::getPolygonCount()
{
    if (ObjType==MCRECONSTRUCTED)
        return vertices.size()/3;
    else
        return int(indexed_vertices.size()/3);
}

int VObject::getVertexCount()
{
    if (ObjType==MCRECONSTRUCTED)
        return vertices.size();
    else
        return indexed_vertices.size();
}

// Animaciones basicas
void VObject::Animate()
{
    if (SHOWTIMEROTATION)
    {
        ShowTimeRotation(1.5, 0.0, 1.0, 0.0);
    }
}

void VObject::ShowTimeRotation(float angle, float x, float y, float z)
{
    rotMatrix.rotate(angle, x, y, z);
}

void VObject::StopShowTimeRotation()
{
    SHOWTIMEROTATION= false;
}

void VObject::LightTweak(int key)
{
    qint64 elapsedTime= LastTime.elapsed();
    float deltaTime= float(elapsedTime)/100.0;

    // Esto es porque no queremos un salto abrupto en el movimiento
    if (deltaTime> 1.0)
        deltaTime= 1.0;

    float UnitsMoved= LightSpeed * deltaTime;

    if (key== Qt::Key_4) // Luz a la Izquierda
        TranslateObj(UnitsMoved, 0.0, 0.0);
    else if (key== Qt::Key_6) // Luz a la Derecha
        TranslateObj(-UnitsMoved, 0.0, 0.0);
    else if (key== Qt::Key_8) // Luz hacia adelante
        TranslateObj(0.0, 0.0, -UnitsMoved);
    else if (key== Qt::Key_2) // Luz hacia atras
        TranslateObj(0.0, 0.0, UnitsMoved);
    else if (key== Qt::Key_1) // Luz hacia arriba
        TranslateObj(0.0, UnitsMoved, 0.0);
    else if (key== Qt::Key_0) // Luz hacia abajo
        TranslateObj(0.0, -UnitsMoved, 0.0);
    else if (key== Qt::Key_Plus) // Mas intensidad
        LightIntensity+= 0.002 * deltaTime;
    else if (key== Qt::Key_Minus) // Menos intensidad
        LightIntensity-= 0.002 * deltaTime;
    else if (key== Qt::Key_9) // Acelerar movimiento
        LightSpeed+= 0.002 * deltaTime;
    else if (key== Qt::Key_3) // Desacelerar movimiento
    {
        LightSpeed-= 0.002 * deltaTime;

        if (LightSpeed<0.0)
            LightSpeed= 0.0;
    }
}

// DEBUG
void VObject::DumpInfo()
{
    printf("ObjName: %s\n", objectName().toStdString().c_str());
    printf("ObjPath: %s\n", ObjPath.toStdString().c_str());
    printf("TexturePath: %s\n", DiffuseTexture.TUrl.toStdString().c_str());

    printf("SMOOTHSHADING: %d\n", SMOOTHSHADING);

    printf("\nVertices: %d\n", vertices.size());
    for (int i=0; i<vertices.size(); i++)
    {
        vec3 auxvertex= vertices.at(i);
        printf("%d: %.4f %.4f %.4f\n", i, auxvertex.x, auxvertex.y, auxvertex.z);
    }

    printf("\nVertices Indexados: %d\n", indexed_vertices.size());
    for (int i=0; i<indexed_vertices.size(); i++)
    {
        vec3 auxvertex= indexed_vertices.at(i);
        printf("%d: %.4f %.4f %.4f\n", i, auxvertex.x, auxvertex.y, auxvertex.z);
    }

    printf("\nCoordenadas UV: %d\n", uvC.size());
    for (int i=0; i<uvC.size(); i++)
    {
        vec2 auxUV= uvC.at(i);
        printf("%d: %.4f %.4f\n", i, auxUV.x, auxUV.y);
    }

    printf("\nCoordenadas UV Indexadas: %d\n", indexed_uvs.size());
    for (int i=0; i<indexed_uvs.size(); i++)
    {
        vec2 auxUV= indexed_uvs.at(i);
        printf("%d: %.4f %.4f\n", i, auxUV.x, auxUV.y);
    }

    printf("\nNormales: %d\n", normals.size());
    for (int i=0; i<normals.size(); i++)
    {
        vec3 auxnormal= normals.at(i);
        printf("%d: %.4f %.4f %.4f\n", i, auxnormal.x, auxnormal.y, auxnormal.z);
    }

    printf("\nNormales Indexadas: %d\n", normals.size());
    for (int i=0; i<indexed_normals.size(); i++)
    {
        vec3 auxnormal= indexed_normals.at(i);
        printf("%d: %.4f %.4f %.4f\n", i, auxnormal.x, auxnormal.y, auxnormal.z);
    }

    printf("\nIndices: %d\n", render_indices.size());
    for (int i=0; i<render_indices.size(); i++)
    {
        printf("%d: %d\n", i, render_indices.at(i));
    }
}
